using System;
using System.Collections.Generic;
using System.Text;
using RapidHDL;

namespace MicroFabric
{
    public class QuickFabric : FabricComponent
    {
        int iPCWidth;
        int iConstWidth;
        int iIRWidth;

        RapidHDL.Fabric.MicroCoreBlock  oMicroCore;
        RapidHDL.Fabric.ProgramROMRAMBlock oProgram;
        MicroCoreTable oCoreTable;

        public QuickFabric(Component poParentComponent, string psName, ClockComponent poClock, NodeVector pnvReset)
            : base(poParentComponent, psName, poClock, pnvReset)
        {
        }


        public override void DefineFabric()
        {
            ContainerBlock.CreateExternalBusInput("EQI", 64);
            ContainerBlock.CreateExternalBusOutput("EQO", 42);

            //Registers and Operators
            ContainerBlock.CreateRegister("A", 32, true);
            ContainerBlock.CreateRegister("B", 32, true);

            ContainerBlock.CreateALU("ADD", ALUType.Add, 32);
            ContainerBlock.CreateALU("AND", ALUType.AndMask, 32);

            // Equality Status Bit
            ContainerBlock.CreateRegister("SR", 1, true);
            ContainerBlock.CreateLogicBlock("EQ", "IN1<32> = IN2<32>");
            ContainerBlock.Join("EQ", "OUT", "SR", "IN");
            ContainerBlock.CreateStatusOutput("EQ", "SR", "OUT");

            //STACK
            ContainerBlock.CreateRAM("STACK", 32, 12, true);// should be bigger to be safe
            ContainerBlock.CreateRegister("SP", 6, true);
            ContainerBlock.CreateALU("SP_INC", ALUType.Inc, 6);
            ContainerBlock.CreateALU("SP_DEC", ALUType.Dec, 6);
            ContainerBlock.Join("SP", "OUT", "STACK", "ADDRESS");
            ContainerBlock.Join("SP", "OUT", "SP_INC", "IN1");
            ContainerBlock.Join("SP", "OUT", "SP_DEC", "IN1");


            // Program Core
            string sROMProgramPath = "c:\\src\\rapidhdl\\extras\\quick_rom.rasm";
            string sRAMProgramPath = "c:\\src\\rapidhdl\\extras\\quick_ram.rasm";
            
            oCoreTable = new _MicroFabric.QuickFabricTable();
            oMicroCore = ContainerBlock.CreateMicroCore("CORE", oCoreTable);
            oProgram = ContainerBlock.CreateProgramROMRAM("PROG", oCoreTable, sROMProgramPath, sRAMProgramPath, 100, 50, 32);

            iPCWidth = oProgram.Inputs["PC"].Width;
            iConstWidth = oProgram.Outputs["CONST"].Width;
            iIRWidth = oProgram.Outputs["IR"].Width;

            ContainerBlock.CreateRegister("C", 32, true);
            ContainerBlock.CreateRegister("PC", iPCWidth, true);
            ContainerBlock.CreateALU("PC_INC", ALUType.Inc, iPCWidth);

            ContainerBlock.Join("PROG", "CONST", "C", "IN");
            ContainerBlock.Join("PC", "OUT", "PC_INC", "IN1");
            ContainerBlock.Join("PROG", "IR", "CORE", "IR");
            ContainerBlock.Join("PC", "OUT", "PROG", "PC");


            // Basic Constants
            ContainerBlock.CreateConstant("ZERO", 0, 32);
            ContainerBlock.CreateConstant("ZERO_BIT", 0, 1);
            ContainerBlock.CreateConstant("ONE_BIT", 1, 1);


            //External FIFO Interface

            ContainerBlock.CreateRegister("QI", 32, true);
            ContainerBlock.CreateRegister("QT", 6, true);
            ContainerBlock.CreateRegister("QO", 32, true);

            ContainerBlock.CreateCombine("E_OUT", 32, 2, 6, 1, 1);
            ContainerBlock.Join("E_OUT", "OUT", "EQO", "IN");
            ContainerBlock.Join("QO", "OUT", "E_OUT", "IN1");
            ContainerBlock.Join("ZERO", "OUT", "E_OUT", "IN2");
            ContainerBlock.Join("ZERO", "OUT", "E_OUT", "IN3");
            ContainerBlock.Join("ZERO_BIT", "OUT", "E_OUT", "IN4");

            ContainerBlock.CreateBreakout("E_IN", 32, 24, 6, 1, 1);
            ContainerBlock.Join("EQI", "OUT", "E_IN", "IN");
            ContainerBlock.Join("E_IN", "OUT1", "QI", "IN");
            ContainerBlock.Join("E_IN", "OUT3", "QT", "IN");

            ContainerBlock.CreateResize("QT_32", 6, 32);
            ContainerBlock.Join("QT", "OUT", "QT_32", "IN");


            ContainerBlock.Join("A", "OUT", "PROG", "IR");
            ContainerBlock.Join("B", "OUT", "PROG", "CONST");
            ContainerBlock.Join("QI", "OUT", "PROG", "ADDRESS");


            DefineControlStates();
        }

        public void DefineControlStates()
        {
            //NOP
            RapidHDL.Fabric.ControlState oControl = ContainerBlock.CreateControlState("NOP");

            //LOAD_A_C
            oControl = ContainerBlock.CreateControlState("LOAD_A_C");
            oControl.Join("C", "OUT", "A", "IN");
            oControl.Activate("A", "LATCH");

            //LOAD_B_C
            oControl = ContainerBlock.CreateControlState("LOAD_B_C");
            oControl.Join("C", "OUT", "B", "IN");
            oControl.Activate("B", "LATCH");

            //PUSH_A
            oControl = ContainerBlock.CreateControlState("PUSH_A");
            oControl.Join("A", "OUT", "STACK", "DATA_IN");
            oControl.Activate("STACK", "WRITE");

            //PULL_A
            oControl = ContainerBlock.CreateControlState("PULL_A");
            oControl.Join("STACK", "DATA_OUT", "A", "IN");
            oControl.Activate("A", "LATCH");

            //PUSH_B
            oControl = ContainerBlock.CreateControlState("PUSH_B");
            oControl.Join("B", "OUT", "STACK", "DATA_IN");
            oControl.Activate("STACK", "WRITE");

            //PULL_A
            oControl = ContainerBlock.CreateControlState("PULL_B");
            oControl.Join("STACK", "DATA_OUT", "B", "IN");
            oControl.Activate("B", "LATCH");


            //LOAD_A_QI
            oControl = ContainerBlock.CreateControlState("LOAD_A_QI");
            oControl.Join("QI", "OUT", "A", "IN");
            oControl.Activate("A", "LATCH");

            //LOAD_QO_A
            oControl = ContainerBlock.CreateControlState("LOAD_QO_A");
            oControl.Join("A", "OUT", "QO", "IN");
            oControl.Activate("QO", "LATCH");

            //PC_INC
            oControl = ContainerBlock.CreateControlState("PC_INC");
            oControl.Join("PC_INC", "OUT", "PC", "IN");
            oControl.Activate("PC", "LATCH");
            oControl.Activate("C", "LATCH");

            //PC_JUMP
            oControl = ContainerBlock.CreateControlState("PC_JUMP");
            oControl.Join("C", "OUT", "PC", "IN");
            oControl.Activate("PC", "LATCH");

            //EQI_READ
            oControl = ContainerBlock.CreateControlState("EQI_READ");
            oControl.Activate("QI", "LATCH");
            oControl.Activate("QT", "LATCH");
            oControl.Activate("EQI", "READ");

            //COMP_A_C
            oControl = ContainerBlock.CreateControlState("COMP_A_C");
            oControl.Join("A", "OUT", "EQ", "IN1");
            oControl.Join("C", "OUT", "EQ", "IN2");
            oControl.Activate("SR", "LATCH");

            //COMP_A_B
            oControl = ContainerBlock.CreateControlState("COMP_A_B");
            oControl.Join("A", "OUT", "EQ", "IN1");
            oControl.Join("B", "OUT", "EQ", "IN2");
            oControl.Activate("SR", "LATCH");

            //ADD_A_C
            oControl = ContainerBlock.CreateControlState("ADD_A_C");
            oControl.Join("A", "OUT", "ADD", "IN1");
            oControl.Join("C", "OUT", "ADD", "IN2");
            oControl.Join("ADD", "OUT", "A", "IN");
            oControl.Activate("A", "LATCH");

            //ADD_A_B
            oControl = ContainerBlock.CreateControlState("ADD_A_B");
            oControl.Join("A", "OUT", "ADD", "IN1");
            oControl.Join("B", "OUT", "ADD", "IN2");
            oControl.Join("ADD", "OUT", "A", "IN");
            oControl.Activate("A", "LATCH");

            //EQO_POST
            oControl = ContainerBlock.CreateControlState("EQO_POST");
            oControl.Activate("EQO", "LATCH");

            //SP_INC
            oControl = ContainerBlock.CreateControlState("SP_INC");
            oControl.Join("SP_INC", "OUT", "SP", "IN");
            oControl.Activate("SP", "LATCH");

            //SP_DEC
            oControl = ContainerBlock.CreateControlState("SP_DEC");
            oControl.Join("SP_DEC", "OUT", "SP", "IN");
            oControl.Activate("SP", "LATCH");

            //A_AND_B
            oControl = ContainerBlock.CreateControlState("A_AND_B");
            oControl.Join("A", "OUT", "AND", "IN1");
            oControl.Join("B", "OUT", "AND", "IN2");
            oControl.Activate("A", "LATCH");

            //PROGRAM
            oControl = ContainerBlock.CreateControlState("PROGRAM");
            oControl.Activate("PROG", "PROGRAM");

            //COMP_QT_C
            oControl = ContainerBlock.CreateControlState("COMP_QT_C");
            oControl.Join("QT_32", "OUT", "EQ", "IN1");
            oControl.Join("C", "OUT", "EQ", "IN2");
            oControl.Activate("SR", "LATCH");
        }


        public void AddSignalViews()
        {
            ContainerBlock.AddSignalView("A", "OUT", SignalFormat.Unsigned);
            ContainerBlock.AddSignalView("B", "OUT", SignalFormat.Unsigned);
            ContainerBlock.AddSignalView("SR", "OUT");
            ContainerBlock.AddSignalView("C", "OUT", SignalFormat.Unsigned);
            ContainerBlock.AddSignalView("PROG", "IR", SignalFormat.Unsigned);
            ContainerBlock.AddSignalView("PC", "OUT", SignalFormat.Unsigned);
            ContainerBlock.AddSignalView("QI", "OUT", SignalFormat.Unsigned);
            ContainerBlock.AddSignalView("QO", "OUT", SignalFormat.Unsigned);
            ContainerBlock.AddSignalView("SP", "OUT", SignalFormat.Unsigned);
            ContainerBlock.AddSignalView("QT", "OUT", SignalFormat.Unsigned);
            ContainerBlock.AddSignalView("EQI", "OUT");
            ContainerBlock.AddSignalView("EQI", "READY");

            oMicroCore.AddSignalViews();
            oProgram.AddSignalViews();
        }

        public void SignalDisplay(HardwareScript poScript, int piClockTime)
        {
            ContainerBlock.SignalDisplay(poScript, piClockTime);
            oMicroCore.SignalDisplay(poScript, piClockTime);
            oProgram.SignalDisplay(poScript, piClockTime);
        }

        public string GetProgramText()
        {
            return oProgram.GetProgramText();
        }

        public Dictionary<int, int> GetProgramAddressToLine()
        {
            return oProgram.GetProgramAddressToLine();
        }

        public void LoadRAMProgram()
        {
            oProgram.LoadRAMProgram();
        }

        public List<string> GetOpCodes()
        {
            List<string> lResult = new List<string>();
            foreach (string sOpCode in oCoreTable.Ops.Keys)
                lResult.Add(sOpCode);

            return lResult;
        }

        public List<int> GetBreakpoints()
        {
            return oProgram.GetBreakpoints();
        }
    }
}
